function TestCtrl($scope) {

  $scope.buildIndexer = function(n) {
    var output = [];
    for (var i = 0; i < n; i++) {
      output.push(i);
    }
    return output;
  }

  $scope.buildErrors = function(n) {
    var output = [];
    for (var i = 0; i < n; i++) {
      output.push('');
    }
    return output;
  }

  $scope.widget = {
    general_messages: DEFAULT_ERROR_MESSAGES,
    vocabulary: BASE_VOCABULARY
  };
  // We build from the bottom up, as this is how information flows in demonstration.html

  // CONTROL FUNCTIONS
  $scope.controlFunctionStrings = DEFAULT_CONTROL_FUNCTION_STRINGS;
  $scope.controlFunctionIndexer = $scope.buildIndexer($scope.controlFunctionStrings.length);
  $scope.submitControlFunctions = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.controlFunctionErrors = $scope.buildErrors($scope.controlFunctionStrings.length);
    var attempt = buildControlFunctionTable($scope.controlFunctionStrings)
    if (attempt.hasOwnProperty('failure')) {
      $scope.controlFunctionErrors[attempt.line] = attempt.failure;
      $scope.controlFunctionSuccess = false;
    } else {
      $scope.widget.control_language = attempt.language;
      $scope.widget.control_model = attempt.model;
      $scope.controlFunctionSuccess = true;
    }
  };
  $scope.submitControlFunctions();

  $scope.widget.mistake_table = [0, 0, 0, 0];

  // MISTAKE TABLE: LAYOUT
  $scope.layoutMistakeStrings = DEFAULT_LAYOUT_MISTAKE_STRINGS;
  $scope.layoutMistakeIndexer = $scope.buildIndexer($scope.layoutMistakeStrings.length);
  $scope.submitLayoutMistakes = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.layoutMistakeErrors = $scope.buildErrors($scope.layoutMistakeStrings.length);
    var attempt = buildMistakeSection('layout', $scope.layoutMistakeStrings, $scope.widget.control_language, DEFAULT_ERROR_MESSAGES);
    if (attempt.hasOwnProperty('failure')) {
      $scope.layoutMistakeErrors = attempt.failure;
      $scope.layoutMistakeSuccess = false;
    } else {
      $scope.widget.mistake_table[0] = attempt;
      $scope.layoutMistakeSuccess = true;
    }
  }

  // MISTAKE TABLE: VARIABLES
  $scope.variableMistakeStrings = DEFAULT_VARIABLE_MISTAKE_STRINGS;
  $scope.variableMistakeIndexer = $scope.buildIndexer($scope.variableMistakeStrings.length);
  $scope.submitVariableMistakes = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.variableMistakeErrors = $scope.buildErrors($scope.variableMistakeStrings.length);
    var attempt = buildMistakeSection('variables', $scope.variableMistakeStrings, $scope.widget.control_language, DEFAULT_ERROR_MESSAGES);
    if (attempt.hasOwnProperty('failure')) {
      $scope.variableMistakeErrors = attempt.failure;
      $scope.variableMistakeSuccess = false;
    } else {
      $scope.widget.mistake_table[1] = attempt;
      $scope.variableMistakeSuccess = true;
    }
  }

  // MISTAKE TABLE: LOGIC
  $scope.logicMistakeStrings = DEFAULT_LOGIC_MISTAKE_STRINGS;
  $scope.logicMistakeIndexer = $scope.buildIndexer($scope.logicMistakeStrings.length);
  $scope.submitLogicMistakes = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.logicMistakeErrors = $scope.buildErrors($scope.logicMistakeStrings.length);
    var attempt = buildMistakeSection('logic', $scope.logicMistakeStrings, $scope.widget.control_language, DEFAULT_ERROR_MESSAGES);
    if (attempt.hasOwnProperty('failure')) {
      $scope.logicMistakeErrors = attempt.failure;
      $scope.logicMistakeSuccess = false;
    } else {
      $scope.widget.mistake_table[2] = attempt;
      $scope.logicMistakeSuccess = true;
    }
  }

  // MISTAKE TABLE: TARGET
  $scope.targetMistakeStrings = DEFAULT_TARGET_MISTAKE_STRINGS;
  $scope.targetMistakeIndexer = $scope.buildIndexer($scope.targetMistakeStrings.length);
  $scope.submitTargetMistakes = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.targetMistakeErrors = $scope.buildErrors($scope.targetMistakeStrings.length);
    var attempt = buildMistakeSection('target', $scope.targetMistakeStrings, $scope.widget.control_language, DEFAULT_ERROR_MESSAGES);
    if (attempt.hasOwnProperty('failure')) {
      $scope.targetMistakeErrors = attempt.failure;
      $scope.targetMistakeSuccess = false;
    } else {
      $scope.widget.mistake_table[3] = attempt;
      $scope.targetMistakeSuccess = true;
    }
  }

  if ($scope.controlFunctionSuccess) {
    $scope.submitLayoutMistakes();
    $scope.submitVariableMistakes();  
    $scope.submitLogicMistakes();
    $scope.submitTargetMistakes();
  }

  // LINE TEMPLATES
  $scope.lineTemplateStrings = DEFAULT_LINE_TEMPLATE_STRINGS;
  $scope.lineTemplateIndexer = $scope.buildIndexer($scope.lineTemplateStrings.length);
  $scope.submitLineTemplates = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.LineTemplateErrors = $scope.buildErrors($scope.lineTemplateStrings.length);
    try {
      $scope.widget.line_templates = buildLineTemplateTable($scope.lineTemplateStrings, BASE_STUDENT_LANGUAGE, BASE_VOCABULARY, DEFAULT_ERROR_MESSAGES);
      $scope.lineTemplateSuccess = true;
    }
    catch (err) {
      $scope.LineTemplateErrors[err.line] = renderError(err, DEFAULT_ERROR_MESSAGES, BASE_STUDENT_LANGUAGE.operators);
      $scope.lineTemplateSuccess = false;
    }
  }
  $scope.submitLineTemplates();

  // QUESTION
  $scope.assumptionsString = 'P&Q';
  $scope.targetString = 'Q&P';
  $scope.submitQuestion = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildError = '';
    $scope.buildSuccess = false;
    $scope.questionError = '';
    try {
      var attempt = buildQuestion($scope.assumptionsString, $scope.targetString, BASE_STUDENT_LANGUAGE, BASE_VOCABULARY);
      $scope.widget.assumptions = attempt.assumptions;
      $scope.widget.results = attempt.results;
      $scope.widget.language = attempt.language;
      $scope.questionSuccess = true;
      $scope.assumptionsDisplay = displayExpressionArray($scope.widget.assumptions, $scope.widget.language.operators);
      $scope.targetDisplay = displayExpression($scope.widget.results[0], $scope.widget.language.operators);
    }
    catch (err) {
      $scope.questionError = renderError(err, DEFAULT_ERROR_MESSAGES, BASE_STUDENT_LANGUAGE.operators);
      $scope.questionSuccess = false;
    }
  }
  $scope.submitQuestion();



  // BUILD PROOF
  $scope.proofString = 'from P&Q we have P\nfrom P&Q we have Q\nfrom Q and P we have Q&P';
  $scope.buildProof = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    var attempt = buildProof($scope.proofString, $scope.widget);
    if (attempt.hasOwnProperty('failure')) {
      $scope.buildError = 'line ' + (attempt.line+1) + ': ' + attempt.failure;
      $scope.buildSuccess = false;
    } else {
      $scope.proof = attempt;
      $scope.buildError = '';
      $scope.buildSuccess = true;
    }
  }
  
  // CHECK PROOF
  $scope.checkProof = function() {
    $scope.checkError = '';
    $scope.checkSuccess = false;
    $scope.buildProof();
    if ($scope.buildSuccess) {
      var attempt = checkProof($scope.proof, $scope.widget);
      if (attempt.hasOwnProperty('failure')) {
        $scope.checkError = 'line ' + (attempt.line+1) + ': ' + attempt.failure;
      } else {
        $scope.checkSuccess = true;
      }
    }
  }

  // LOCAL CHECK (for testing only)
  $scope.localCheck = function() {
    $scope.buildProof();
    $scope.localCheck = 'mistake not found'
    var parameters = {
      proof: $scope.proof,
      assumptions: $scope.widget.assumptions,
      target: $scope.widget.results[0]
    };
    for ( var i = 0; i < $scope.widget.mistake_table.length; i++) {
      for (var j = 0; j < $scope.widget.mistake_table[i].entries.length; j++) {
        var mistake = $scope.widget.mistake_table[i].entries[j];
        if (mistake.name === $scope.mistakeName) {
          $scope.localCheck = evaluate(mistake.occurs, {n: parseInt($scope.line)}, $scope.widget.control_model, parameters, {});
        }
      }
    }
  }
} 


